Recoil > Loadable
#Recoil #宣言的UIの設計レシピ #状態管理(GUI) #Server_State
Recoilの非同期ステートの抽象クラス
https://recoiljs.org/docs/api-reference/core/Loadable
https://github.com/facebookexperimental/Recoil/blob/main/packages/recoil/adt/Recoil_Loadable.js
Readerモナド的なモノ
mapなどのインタフェースが生えていて"値"と"計算"を区別できる
Recoilの設計者がOCamlの使い手らしく、そこから着想を得たものと思われる
koushisa.icon的にRecoilで一番評価してる
ReduxやRxで原始的に実装しようとするとコード量が大変なことになる
リモートデータとUIの状態遷移パターン
非同期の状態をタグ付きユニオン型で、代数的データ型(ADT)っぽく(loading, hasValue, hasError)表現できる
状態を型安全にハンドリングできる
これ一つでThe five UI statesを満たす実装が簡単にできる
https://recoiljs.org/docs/api-reference/utils/waitForNone/#incremental-loading-example
この例では、複数のレイヤーを持つグラフをレンダリングします。
各レイヤーは、潜在的に高価なデータクエリを持っています。
まだ保留中の各レイヤーのスピナーを使用してすぐにチャートをレンダリングし、そのレイヤーのデータが届くと、チャートを更新して各新規レイヤーを追加します。
もしクエリにエラーがあるレイヤーがあれば、そのレイヤーだけがエラーメッセージを表示し、残りのレイヤーはレンダリングを継続します。
code:typescript
function MyChart({layerQueries}: {layerQueries: Array<RecoilValue<Layer>>}) {
// layerQueriesがデータフェッチを行う非同期処理
// waitForNoneは実行時のコンテキストを引き継いでSelectorの状態をLoadableで返す関数
// useRecoilValueLoadable(layerQueries)でもいいはずなのだが、サンプルがなぜこういう書き方なのかは謎
const layerLoadables = useRecoilValue(waitForNone(layerQueries));
return (
<Chart>
{layerLoadables.map((layerLoadable, i) => {
switch (layerLoadable.state) {
// 非同期処理の結果が返ってきたとき
case 'hasValue':
return <Layer key={i} data={layerLoadable.contents} />;
// 非同期処理の結果がエラーなとき
case 'hasError':
return <LayerErrorBadge key={i} error={layerLoadable.contents} />;
// 非同期処理中
case 'loading':
return <LayerWithSpinner key={i} />;
}
})}
</Chart>
);
}
SwiftでいうResult型っぽさもあるkoushisa.icon
Loadable.getValueでSuspenseモードで使ってもいいし、上述のLoadable.stateによってViewを切り替えてもいい
waitFor*とLoadableを組み合わせればReact > Concurrent Featuresを実現できるということ
Loadable.mapつかうとインラインにtransform的な実装が出来る
コンポーネントの中でもSelectorの中でも使える
code:.ts
const usersWithCount = useRecoilValueLoadable(usersState).map(users => {
return {
data: users,
count: users.length
}
}).getValue()
// ->
usersWithCount.data
usersWithCount.count